/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
/*
 * Projects
 * Copyright (C) Arnaud Duval 2010 <arnaud.duval@gmail.com>
 * 
 * Projects is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Projects is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "track-point.hpp"
#include <cmath>
#include <sstream>
#include <iostream>
#include <cassert>


#define EARTH_RADIUS 6371.

///@brief default constructor
TrackPoint::TrackPoint()
{
    lat = 0.;
    lon = 0.;
    ele = 0.;
    speed = 0.;
}

///@brief Copy constructor
///@param t TrackPoint object to copy
TrackPoint::TrackPoint(const TrackPoint& t)
{
    lat = t.lat;
    lon = t.lon;
    ele = t.ele;
    speed = t.speed;
    time = t.time;
}

///@brief Constructor with argument
///@param Dlat Latitude in degrees
///@param Dlon Longitude in degrees
///@param Dele Elemvation in meters
///@param Dspeed Speed in m/s
///@param Dtime time
TrackPoint::TrackPoint(double Dlat, double Dlon, double Dele,
                       double Dspeed, DateTime Dtime)
{
    lat = Dlat;
    lon = Dlon;
    ele = Dele;
    speed = Dspeed;
    time = Dtime;
}

///@brief Destructor
TrackPoint::~TrackPoint()
{

}

///@brief Overload of = operator
///@param t TrackPoint object to copy
TrackPoint &TrackPoint::operator=(const TrackPoint& t)
{
    lat = t.lat;
    lon = t.lon;
    ele = t.ele;
    speed = t.speed;
    time = t.time;
    return (*this);
}

///@brief Read data from a XML Node
///@param node Pointer to XML node
bool TrackPoint::ReadXMLNode(xmlpp::Node* node)
{
    if(node->get_name() != "trkpt")
        return false;
    ///@brief Obtaining attributes
    const xmlpp::Element* nodeElement = dynamic_cast<const xmlpp::Element*>(node);
    const xmlpp::Element::AttributeList& attributes = nodeElement->get_attributes();
    for(xmlpp::Element::AttributeList::const_iterator iter_attr = attributes.begin(); iter_attr != attributes.end(); ++iter_attr)
    {
        const xmlpp::Attribute* attribute = *iter_attr;
        if(attribute->get_name() == "lat")
        {
            std::istringstream stream(attribute->get_value());
            stream >> lat;
            if(stream.fail())
            {
                std::cerr << "Problem while reading trkpt attributes\n";
            }
        }
        if(attribute->get_name() == "lon")
        {
            std::istringstream stream(attribute->get_value());
            stream >> lon;
            if(stream.fail())
            {
                std::cerr << "Problem while reading trkpt attributes\n";
            }
        }
    }
    //Reading children of trkpt
    xmlpp::Node::NodeList listTrackPtChildren = node->get_children();
    for(xmlpp::Node::NodeList::iterator iter = listTrackPtChildren.begin(); iter != listTrackPtChildren.end(); ++iter)
    {
        xmlpp::Node* trackPtChild = dynamic_cast<const xmlpp::Node*>(*iter);
        if(trackPtChild->get_name() == "ele")
        {
            std::istringstream stream(((xmlpp::TextNode*)(*((trackPtChild->get_children()).begin())))->get_content());
            stream >> ele;
            if(stream.fail())
            {
                std::cout << "Problem while reading ele value\n";
            }
        }
        if(trackPtChild->get_name() == "time")
        {
            std::string timeString = ((xmlpp::TextNode*)(*((trackPtChild->get_children()).begin())))->get_content();
            time.SetWithFormat(timeString, "yyyy-mm-ddThh:nn:ssZ");
        }
        if(trackPtChild->get_name() == "speed")
        {
            std::istringstream stream(((xmlpp::TextNode*)(*((trackPtChild->get_children()).begin())))->get_content());
            stream >> speed;
            if(stream.fail())
            {
                std::cout << "Problem while reading speed value\n";
            }
        }
    }
    
    return true;
}

///@brief Return Latitude
double TrackPoint::GetLat()
{
	return lat;
}

///@brief Return Longitude
double TrackPoint::GetLon()
{
	return lon;
}

///@brief return time
DateTime TrackPoint::GetTime()
{
	return time;
}

///@brief Return elevation value
double TrackPoint::GetElevation()
{
	return ele;
}

///@brief Return speed value
double TrackPoint::GetSpeed()
{
	return speed;
}

///@brief Write to XML file
///@param fout out stream to XML file
void TrackPoint::WriteXML(std::ofstream &fout)
{
	fout << "\t\t\t\t\t\t<ele>" << ele << "</ele>\n";
	fout << "\t\t\t\t\t\t<time>" << time.AsString("yyyy-mm-ddThh:nn:ssZ") << "</time>\n";
	fout << "\t\t\t\t\t\t<desc>lat.=" << lat << ", lon.=" << lon << ", Alt.="
		<< ele << "m. Speed=" << speed*3.6 << "m/h.</desc>\n";
	///@warning Desc tag is based on Holux GPSport  one. It seems there is a problem in the choice of separatof (. or ,) and in the unity of speed (m/h instead of km/h)
	fout << "\t\t\t\t\t\t<speed>" << speed << "</speed>\n";
}

///@brief Set elevation value
///@param Delevation Elevation value to be set
void TrackPoint::SetElevation(double Delevation)
{
	ele = Delevation;
}

///@brief Set the speed value
///@param Dspeed Speed value to be set
void TrackPoint::SetSpeed(double Dspeed)
{
	speed = Dspeed;
}

///@brief Compute the distance between two TrackPoint
///@param a First TrackPoint
///@param b Second TrackPoint
double Distance(TrackPoint a, TrackPoint b)
{
	double lat1 = a.lat*M_PI/180.;
	double lat2 = b.lat*M_PI/180.;
	double lon1 = a.lon*M_PI/180.;
	double lon2 = b.lon*M_PI/180.;
		
	return EARTH_RADIUS*2.*asin(sqrt(pow(sin((lat1-lat2)/2.),2.) + cos(lat1)*cos(lat2)*pow(sin((lon1-lon2)/2),2.)));
}

///@brief Overload of == operator
///@param p1 left hand operand
///@param p2 right hand operand
bool operator==(const TrackPoint& p1, const TrackPoint& p2)
{
	if(p1.lat != p2.lat)
		return false;
	if(p1.lon != p2.lon)
		return false;
	if(p1.ele != p2.ele)
		return false;
	if(p1.speed != p2.speed)
		return false;
	if(p1.time != p2.time)
		return false;
}
///@brief Overload of != operator
///@param p1 left hand operand
///@param p2 right hand operand
bool operator!=(const TrackPoint& p1, const TrackPoint& p2)
{
	return !(p1 == p2);
}

///@brief Set the time value
///@param Dtime DateTime value to set
void TrackPoint::SetTime(DateTime Dtime)
{
    time = Dtime;
}


///@brief Write a part of the informations contained in the track point to the standard output (for debug purpose)
void TrackPoint::WriteForDebug()
{
    std::cout << lat << "\t" << lon << "\t" << ele << "\n";
}

///@brief Set the latitude
///@param latitude Latitude value to set
void TrackPoint::SetLat(double latitude)
{
    assert(latitude >= -90.);
    lat = latitude;
}

///@brief Set the longitude
///@param longitude Longitude value to be set
void TrackPoint::SetLon(double longitude)
{
    assert(longitude >= -180.);
    lon = longitude;
}